package ucd.csi.comp30220.fall2011.oteop.webapp.client;

import java.util.HashMap;
import java.util.ArrayList;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.maps.client.InfoWindow;
import com.google.gwt.maps.client.InfoWindowContent;
import com.google.gwt.maps.client.MapOptions;
import com.google.gwt.maps.client.MapType;
import com.google.gwt.maps.client.MapWidget;
import com.google.gwt.maps.client.control.ScaleControl;
import com.google.gwt.maps.client.control.SmallZoomControl3D;
import com.google.gwt.maps.client.event.MarkerClickHandler;
import com.google.gwt.maps.client.geom.LatLng;
import com.google.gwt.maps.client.geom.Point;
import com.google.gwt.maps.client.geom.Size;
import com.google.gwt.maps.client.overlay.Icon;
import com.google.gwt.maps.client.overlay.Marker;
import com.google.gwt.maps.client.overlay.MarkerOptions;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.RootLayoutPanel;

/**
 * Entry point classes define <code>onModuleLoad()</code>.
 */
public class Viewer implements EntryPoint {

    private final ViewerServiceAsync service = GWT.create(ViewerService.class);
    private final LatLng centre = LatLng.newInstance(53.455349, -7.437744);
    private final HashMap<String,Marker> markers = new HashMap<String,Marker>();
    private final HashMap<Marker,String> content = new HashMap<Marker,String>();
    private MapWidget map;

    // GWT module entry point method.
    public void onModuleLoad() {
        MapOptions mapOptions = MapOptions.newInstance();
        mapOptions.setMapTypes(new ArrayList<MapType>() { {
                add(MapType.getPhysicalMap());
            } });

        map = new MapWidget(centre, 2, mapOptions);
        map.setSize("100%", "100%");
        map.setZoomLevel(8);

        map.addControl(new SmallZoomControl3D());
        map.addControl(new ScaleControl());
        
        RootLayoutPanel.get().add(map);
        
        new Timer() {
            public void run() {
                service.getAllFlightStatus(
                    new AsyncCallback<Info[]>() {
                        public void onFailure(Throwable caught) {
                            // TODO what should I do here?
                        }
                        public void onSuccess(Info[] infos) {
                            update(infos);
                        }
                    });
            }
        }.scheduleRepeating(1000);
    }
    
    private void update(Info[] infos) {
        for (Info info : infos) {
            Marker marker = markers.get(info.getId());
            if (marker == null) {
                marker = createMarker();
                map.addOverlay(marker);
                markers.put(info.getId(), marker);
            }
            marker.setImage(info.getIcon());
            marker.setLatLng(LatLng.newInstance(
                    info.getLatitude(), info.getLongitude())); 
            content.put(marker, info.getMessage());
        }
        if (infos.length != markers.size()) {
            HashMap<String,Marker> marks = new HashMap<String,Marker>();
            for (Info info : infos) {
                marks.put(info.getId(), markers.get(info.getId()));
            }
            ArrayList<String> delete = new ArrayList<String>();
            for (String id : markers.keySet()) {
                if (marks.get(id) == null) {
                    Marker marker = markers.get(id);
                    map.removeOverlay(marker);
                    content.remove(marker);
                    delete.add(id);
                }
            }
            for (String id : delete) {
                markers.remove(id);
            }
        }
    }
    
    private Marker createMarker() {
        Icon icon = Icon.newInstance("images/blueplane_t_n.png");
        icon.setIconSize(Size.newInstance(42, 42));
        icon.setIconAnchor(Point.newInstance(16, 16));
        icon.setInfoWindowAnchor(Point.newInstance(16, 16));
        
        MarkerOptions options = MarkerOptions.newInstance();
        options.setIcon(icon);
        
        final Marker marker = new Marker(centre, options);
        marker.addMarkerClickHandler(new MarkerClickHandler() {
            public void onClick(MarkerClickEvent event) {
                InfoWindow info = map.getInfoWindow();
                info.open(marker,
                    new InfoWindowContent(new HTML(content.get(marker))));
            }
        });
        return marker;
    }
}
